package com.android.camera.one.v2.sharedimagereader;

import static com.android.camera.one.v2.core.ResponseListeners.forFinalMetadata;
import static com.android.camera.one.v2.core.ResponseListeners.forTimestamps;

import com.android.camera.async.HandlerFactory;
import com.android.camera.async.Lifetime;
import com.android.camera.async.Observable;
import com.android.camera.async.Observables;
import com.android.camera.async.Updatable;
import com.android.camera.debug.Log;
import com.android.camera.one.v2.IReprocessExcutor;
import com.android.camera.one.v2.camera2proxy.AndroidImageWriterProxy;
import com.android.camera.one.v2.camera2proxy.CameraDeviceProxy;
import com.android.camera.one.v2.camera2proxy.CameraDeviceRequestBuilderFactory;
import com.android.camera.one.v2.camera2proxy.ImageProxy;
import com.android.camera.one.v2.camera2proxy.ImageReaderProxy;
import com.android.camera.one.v2.camera2proxy.ImageWriterProxy;
import com.android.camera.one.v2.camera2proxy.TotalCaptureResultProxy;
import com.android.camera.one.v2.core.RequestBuilder;
import com.android.camera.one.v2.core.RequestTemplate;
import com.android.camera.one.v2.core.ResponseListener;
import com.android.camera.one.v2.core.ResponseListeners;
import com.android.camera.one.v2.imagesaver.MetadataImage;
import com.android.camera.one.v2.initialization.ICloserManager;
import com.android.camera.one.v2.sharedimagereader.imagedistributor.ImageDistributor;
import com.android.camera.one.v2.sharedimagereader.imagedistributor.ImageDistributorFactory;
import com.android.camera.one.v2.sharedimagereader.imagedistributor.ImageStream;
import com.android.camera.one.v2.sharedimagereader.metadatasynchronizer.MetadataPool;
import com.android.camera.one.v2.sharedimagereader.metadatasynchronizer.MetadataPoolFactory;
import com.android.camera.one.v2.sharedimagereader.ringbuffer.DynamicRingBufferFactory;
import com.android.camera.one.v2.sharedimagereader.ticketpool.FiniteTicketPool;
import com.android.camera.one.v2.sharedimagereader.ticketpool.TicketPool;
import com.google.common.util.concurrent.ListenableFuture;

import android.hardware.camera2.CameraAccessException;
import android.view.Surface;

public class ReprocessExcutorFactory implements IReprocessExcutor {
	private final Log.Tag TAG = new Log.Tag("ReprocessImgRdFct");
	
	private final CameraDeviceProxy mDevice;
	private final ImageWriterProxy mInputImageWriter;
	private final ImageStream mJpegReprocessStream;
    private final MetadataPool mJpegReprocessMetadataPool;
    private final Observable<Integer> mAvailableImageCount;
    private final ResponseListener mResponseListener;
    private final ICloserManager mCloserManager;
	
	public ReprocessExcutorFactory(Lifetime lifetime, CameraDeviceProxy device, 
			final MetadataPool metadataPool, Surface inputSurface, 
			ImageReaderProxy jpegImageReader, HandlerFactory handlerFactory, 
			ICloserManager closerManager) {
		mCloserManager = closerManager;
		mDevice = device;
		mInputImageWriter = AndroidImageWriterProxy.newInstance(inputSurface, 2);
        mInputImageWriter.setOnImageReleasedListener(
                new ImageWriterProxy.OnImageReleasedListener() {
                    @Override
                    public void onImageReleased(ImageWriterProxy writer) {
                        Log.v(TAG, "ImageWriter.OnImageReleasedListener onImageReleased()");
                    }
                }, null);
        lifetime.add(mInputImageWriter);
        
        ImageDistributorFactory imageDistributorFactory = new ImageDistributorFactory(lifetime,
        		jpegImageReader, handlerFactory);
        ImageDistributor imageDistributor = imageDistributorFactory.provideImageDistributor();
        Updatable<Long> globalTimestampQueue = imageDistributorFactory
                .provideGlobalTimestampCallback();
        
        int jpegMaxImages = jpegImageReader.getMaxImages();
        Log.v(TAG, "ReprocessImageReaderFactory "+jpegMaxImages);
        TicketPool rootTicketPool = new FiniteTicketPool(jpegMaxImages);
        
        DynamicRingBufferFactory ringBufferFactory = new DynamicRingBufferFactory(
                new Lifetime(lifetime), rootTicketPool, Observables.of(jpegMaxImages+2));
        
        MetadataPoolFactory metadataPoolFactory = new MetadataPoolFactory(
        		metadataPool, ringBufferFactory.provideRingBufferInput());

        mJpegReprocessStream = new ImageStreamImpl(
                ringBufferFactory.provideRingBufferOutput(),
                metadataPoolFactory.provideImageQueue(),
                imageDistributor, jpegImageReader.getSurface());
        lifetime.add(mJpegReprocessStream);

        mJpegReprocessMetadataPool = metadataPoolFactory.provideMetadataPool();

        mAvailableImageCount = ringBufferFactory.provideTicketPool().getAvailableTicketCount();

        // Create a ResponseListener which updates the global timestamp queue
        // and the metadata callback.
        mResponseListener = ResponseListeners.forListeners(
        		new ResponseListener() {
        			@Override
        		    public void onStarted(long timestamp) {
        				Log.v(TAG, "createReprocessRequest -> "+timestamp);
        				mCloserManager.discreaseCloserCounter();
        			}
        		},
                forTimestamps(globalTimestampQueue),
                forFinalMetadata(metadataPoolFactory.provideMetadataCallback()));
	}
	
	@Override
	public RequestBuilder createReprocessRequest(ImageProxy imageProxy, 
			TotalCaptureResultProxy totalCaptureResultProxy) throws CameraAccessException {
		Log.v(TAG, "createReprocessRequest E");
		if (mCloserManager.isClosed()) return null;
		mCloserManager.increaseCloserCounter();
		RequestTemplate reprocessTemplate = new RequestTemplate(
                new CameraDeviceRequestBuilderFactory(mDevice));
		RequestBuilder reprocesssRequest = reprocessTemplate.create(totalCaptureResultProxy);
		mInputImageWriter.queueInputImage(imageProxy);
		reprocesssRequest.addResponseListener(mResponseListener);
		reprocesssRequest.addStream(mJpegReprocessStream);
        Log.v(TAG, "createReprocessRequest X");
        return reprocesssRequest;
	}
	
	@Override
	public MetadataImage getNext() {
		try {
			ImageProxy imgProxy = mJpegReprocessStream.getNext();
			if (imgProxy == null) return null;
			ListenableFuture<TotalCaptureResultProxy> metadata = 
					mJpegReprocessMetadataPool.removeMetadataFuture(imgProxy.getTimestamp());
			
			return new MetadataImage(imgProxy, metadata);
		} catch (Exception ex) {
			Log.v(TAG, "getNext ex = "+ex.toString());
			return null;
		}
	}
	
	@Override
	public Observable<Integer> provideAvailableImageCount() {
        return mAvailableImageCount;
    }
}
